{
Traviesadora beta 0.2.0.13.33 (30.11.2008)
LastMod: 20.03.2009 by Prof1983

Copyright 2008, travieorama@googlemail.com

This file is part of the taviesadora project

taviesadora is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

taviesadora is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
}
unit RosterDB;

interface

uses
  Classes, SysUtils, Forms, Dialogs, Presence, NodeItem, RosterGlobals;

type
  TCBFireRosterViewAction = (cbRosterChange,cbPresenceChange,cbAvailChange,cbPresenceChanged,cbAvatarChanged,cbClose);
  TCallBackItem = record
    jid: string;
    CallBackObj: TObject;
  end;
  PCallBackItem = ^TCallBackItem;
  TRosterDB = class
  public
    constructor Create();
    destructor Destroy(); override;
    procedure ClearJabber();
    procedure clearNobra();
    procedure StartAddModeJabber();
    procedure StartAddModeNobra();
    procedure EndAddModeJabber();
    procedure EndAddModeNobra();
    procedure Delete(index: Integer);
    procedure AddNobra(ID: string; Nick: string; Vib: Boolean; Blocked, WhiteListed: Boolean);
    procedure PresenceChangeJabber(ritem: TJabberRosterItem; Presence: TJabberPres);
    procedure MyAvatarChanged();
    procedure AvailChangeJabber(ritem: TJabberRosterItem; Presence: TJabberPres);
    procedure RegisterCallBackChat(Chat: TObject; jid: string);
    procedure UnRegisterCallBackChat(Chat: TObject);
    procedure RegisterCallBackRosterView(RosterView: TObject);
    procedure UnRegisterCallBackRosterView(RosterView: TObject);
    procedure SetOwnContactState(ContactState: TContactState; NetType: TNetType);
    procedure SetOwnJID(jid: string);
    procedure SetOwnNick(Nick: string);
    function GetOwnContactData: TContactData;
    function GetID(jid: string): Integer;
    function GetPresenceByJID(jid: string): TContactData;
    function GetPresenceByID(ID: Integer): PContactData;
    function Count(): Integer;
    procedure PresenceChanged();
  private
    RosterLib: TList;
    RosterChangedAfterNobraUpdate: Boolean;
    CallBackListChat: TList;
    CallBackListRosterViewer: TList;
    OwnjabberContactData: TContactData;
    OwnNobraContactData: TContactData;
    AddingModeJabber: Boolean;
    AddingModeNobra: Boolean;
    function SearchForId(id: string): Integer;
    function GetNick(jid, Nick: string): string;
    function GetState(ritem: TJabberRosterItem; Presence: TJabberPres): TContactState;
    function SearchCallBackjid(jid: string): Integer;
    procedure FireCallBackChat(jid: string; NewContactState, OldContactState: TContactState);
    function AddJabber(ritem: TJabberRosterItem; Presence: TJabberPres): Integer;
    procedure FireCallBackRosterView(ContactData: PContactData; Action: TCBFireRosterViewAction);
    procedure ClearCallBack();
    procedure clear(nt: TNetType);
  end;

implementation

uses
  Jabber, ChatDistributer, RosterViewer, JabberID, TraviesadoraCoreImpl;

function SearchCallBackObj(Obj: TObject; CallBackList: TList): Integer;
var
  cbItem: PCallBackItem;
  i: Integer;
begin
  Result := -1;
  for i := 0 to CallBackList.Count-1 do
  begin
    cbItem := CallBackList[i];
    if (cbItem.CallBackObj = Obj) then
    begin
      Result := i;
      Break;
    end;
  end;
end;

constructor TRosterDB.Create();
begin
  inherited Create;
  RosterLib := TList.Create;
  CallBackListChat := TList.Create;
  CallBackListRosterViewer := TList.Create;
  AddingModeJabber := False;
  AddingModeNobra := False;
  SetOwnJID('');
  SetOwnNick('');
  SetOwnContactState(csOffline, ntAll);
end;

destructor TRosterDB.Destroy();
begin
  ClearCallBack();
  clear(ntAll);
  FireCallBackRosterView(nil, cbClose);
  RosterLib.Free();
  CallBackListChat.Free();
  CallBackListRosterViewer.Free();
  inherited Destroy;
end;

procedure TRosterDB.Clear(nt: TNetType);
var
  i: Integer;
  ContactInfo: PContactData;
begin
  i := 0;
  while i < RosterLib.Count do
  begin
    ContactInfo := RosterLib[i];
    if (nt = ntAll) or (ContactInfo.NetType = nt) then
    begin
      dispose(ContactInfo);
      RosterLib.delete(i)
    end
    else
      Inc(i);
  end;
  FireCallBackRosterView(nil, cbRosterChange);
end;

procedure TRosterDB.Delete(index: Integer);
var
  ContactInfo: PContactData;
begin
  ContactInfo := RosterLib[index];
  dispose(ContactInfo);
  RosterLib.Delete(index);
  FireCallBackRosterView(ContactInfo, cbRosterChange);
end;

function TRosterDB.SearchForId(id: string): Integer;
var
  i: Integer;
  found: Boolean;
  ContactInfo: PContactData;
begin
  Result := -1;
  id := LowerCase(id);
  found := False;
  for i := 0 to RosterLib.Count-1 do
  begin
    ContactInfo := RosterLib[i];
    if (ContactInfo.UserId = id) then
    begin
      result := i;
      found := True;
      break;
    end;
  end;
  if not found then
    result := -1;
end;

function TRosterDB.AddJabber(rItem: TJabberRosterItem; Presence: TJabberPres): Integer;
var
  ContactInfo: PContactData;
begin
  new(ContactInfo);
  if Assigned(Presence) then
  begin
    ContactInfo.UserId := LowerCase(Presence.fromJid.full);
    ContactInfo.nick := GetNick(Presence.fromJid.jid,Presence.fromJid.userDisplay);
    ContactInfo.resource := Presence.fromJid.resource;
  end
  else
  begin
    ContactInfo.UserId := lowercase(ritem.Jid.full);
    ContactInfo.nick := GetNick(ritem.Jid.jid,ritem.Text);
    ContactInfo.resource := ritem.Jid.resource;
  end;
  TravCore.JabberObj.DownloadProfil(ContactInfo.UserId);
  ContactInfo.status := GetState(ritem,Presence);
  ContactInfo.NetType := ntJabber;
  ContactInfo.imgIndex := ord(ContactInfo.status);
  ContactInfo.whiteListed := false;
  result := RosterLib.Add(ContactInfo);
  FireCallBackRosterView(ContactInfo,cbRosterChange);
end;

procedure TRosterDB.EndAddModeNobra;
var
  i:integer;
  ContactInfo:PContactData;
begin
  AddingModeNobra := false;
  i := 0;
  while i < RosterLib.Count do begin
    ContactInfo := RosterLib[i];
    if (ContactInfo.NetType = ntNobraNet) and (ContactInfo.status = csDontKnow) then begin
      Delete(i);
      RosterChangedAfterNobraUpdate := true;
    end else
      inc(i);
  end;
  if RosterChangedAfterNobraUpdate then begin
    FireCallBackRosterView(nil,cbPresenceChange);
  end;
end;

procedure TRosterDB.StartAddModeNobra;
var
  ContactInfo:PContactData;
  i:integer;
begin
  RosterChangedAfterNobraUpdate := false;
  AddingModeNobra := true;
  for i := 0 to RosterLib.Count-1 do begin
    ContactInfo := RosterLib[i];
    if ContactInfo.NetType = ntNobraNet then
      ContactInfo.status := csDontKnow;
  end;
end;

procedure TRosterDB.AddNobra(ID, Nick: string;Vib:boolean;blocked,whitelisted:boolean);
var
  ContactInfo:PContactData;
  i:integer;
begin
  ID := lowercase(ID);
  i := SearchForId(ID);
  if i = -1 then begin
    new(ContactInfo);
    ContactInfo.UserId := ID;
    ContactInfo.nick := Nick;
    ContactInfo.resource := '';
    ContactInfo.NetType := ntNobraNet;
    RosterLib.add(ContactInfo);
    RosterChangedAfterNobraUpdate := true;
  end else begin
    ContactInfo := RosterLib[i];
  end;
  ContactInfo.whiteListed := whitelisted;
  if blocked then
    ContactInfo.status := csBlocked
  else begin
    if vib then
      ContactInfo.status := csVib
    else
      ContactInfo.status := csAvailable;
  end;
  ContactInfo.imgIndex := ord(ContactInfo.status);
end;

procedure TRosterDB.PresenceChangeJabber(ritem: TJabberRosterItem; Presence: TJabberPres);
var
  i: Integer;
  ContactInfo: PContactData;
  CS: TContactState;
  oldCS: TContactState;
  s: string;
  t: string;
begin
  if Assigned(ritem) then
  begin
    if AddingModeJabber then
    begin
      AddJabber(ritem,Presence);
    end
    else
    begin
      i := SearchForId(ritem.Jid.full);
      if (i > -1) then
      begin
        if ritem.Subscription = 'remove' then
        begin
          Delete(i);
        end
        else
        begin
          CS := GetState(ritem,Presence);
          ContactInfo := RosterLib[i];
          if Assigned(Presence) then
          begin
            ContactInfo.Resource := Presence.fromJid.resource;
          end;
          oldCS := ContactInfo.status;
          ContactInfo.status := CS;
          ContactInfo.imgIndex := ord(CS);
          FireCallBackChat(ContactInfo.UserId,CS,oldCS);
          FireCallBackRosterView(ContactInfo,cbPresenceChange);
        end;
      end
      else
      begin
        i := SearchForId(ritem.Jid.jid);
        if (i <= 1) then
        begin
          s := lowercase(OwnjabberContactData.UserId);
          System.delete(s,pos('/',s),length(s));
          t := lowercase(ritem.Jid.full);
          System.delete(t,pos('/',t),length(t));
          if (t <> s) then
            AddJabber(ritem, Presence);
        end;
      end;
    end;
  end;
end;

procedure TRosterDB.AvailChangeJabber(ritem: TJabberRosterItem; Presence: TJabberPres);
var
  i: Integer;
  ContactInfo: PContactData;
  CS: TContactState;
begin
  if Assigned(ritem) then
  begin
    i := SearchForId(ritem.Jid.full);
    if (i > -1) then
    begin
      CS := GetState(ritem,Presence);
      ContactInfo := RosterLib[i];
      FireCallBackChat(ritem.Jid.full,CS,ContactInfo.status);
      ContactInfo.imgIndex := ord(CS);
      ContactInfo.status := CS;
      FireCallBackRosterView(ContactInfo,cbAvailChange);
    end;
  end;
end;

function TRosterDB.GetPresenceByID(ID: Integer): PContactData;
begin
  Result := RosterLib[ID];
end;

function TRosterDB.GetID(jid: string): Integer;
begin
  Result := SearchForId(jid);
end;

function TRosterDB.GetPresenceByJID(jid: string): TContactData;
var
  i: Integer;
begin
  i := SearchForId(jid);
  if i > -1 then
    result := GetPresenceByID(i)^
  else begin
    system.delete(jid,pos('/',jid),length(jid));
    i := SearchForId(jid);
    if i > -1 then
      result := GetPresenceByID(i)^
    else begin
      Result.nick := '';
      Result.UserId := '';
      Result.resource := '';
      Result.status := csDontKnow;
      Result.imgIndex := -1;
      Result.NetType := ntAll;
      Result.whiteListed := false;
    end;
  end;
end;

function TRosterDB.GetState(ritem: TJabberRosterItem;Presence:TJabberPres):TContactState;
begin
  if Presence <> nil then begin
    if (ritem.subscription = 'both') or (ritem.subscription = 'to') then begin
      if ritem.isonline then
        result := StringToPresence(Presence.Show)
      else
        result := csOffline;
    end else
      result := csPending;
  end else begin
    if (ritem.subscription = 'both') or (ritem.subscription = 'to') then begin
      if ritem.isonline then
        result := csAvailable
      else
        result := csOffline;
    end else
      result := csPending;
  end;
end;

procedure TRosterDB.EndAddModeJabber;
begin
  AddingModeJabber := false;
end;

procedure TRosterDB.StartAddModeJabber;
begin
  AddingModeJabber := true;
  ClearJabber;
end;

function TRosterDB.count: integer;
begin
  if (OwnjabberContactData.status <> csOffline)  or (OwnNobraContactData.status <> csOffline)  then
    result := RosterLib.Count
  else
    result := 0;
end;

procedure TRosterDB.MyAvatarChanged;
begin
        FireCallBackRosterView(nil,cbAvatarChanged);
end;

procedure TRosterDB.SetOwnContactState(ContactState: TContactState; NetType: TNetType);
var
  cd: TContactData;
  ya_fired: Boolean;
begin
  ya_fired := false;
  if (nettype = ntAll) or (nettype = ntJabber) then
  begin
    if OwnjabberContactData.status <> ContactState then
    begin
      cd.status := OwnjabberContactData.status;
      cd.UserId := '';
      OwnjabberContactData.status := ContactState;
      OwnjabberContactData.imgIndex := ord(ContactState);
      FireCallBackRosterView(@cd,cbPresenceChanged);
      ya_fired := true;
    end;
  end;
  if (nettype = ntAll) or (nettype = ntNobraNet) then
  begin
    if OwnNobraContactData.status <> ContactState then
    begin
      cd.status := OwnNobraContactData.status;
      cd.UserId := '';
      OwnNobraContactData.status := ContactState;
      OwnNobraContactData.imgIndex := ord(ContactState);
      if not ya_fired then
        FireCallBackRosterView(@cd,cbPresenceChanged);
    end;
  end;
end;

procedure TRosterDB.SetOwnJID(jid: string);
begin
  OwnjabberContactData.UserId := jid;
end;

procedure TRosterDB.SetOwnNick(Nick: string);
begin
  OwnjabberContactData.nick := NIck;
end;

function TRosterDB.GetOwnContactData: TContactData;
begin
  result := OwnjabberContactData;
  if (OwnNobraContactData.status <> csOffline) and (OwnjabberContactData.status = csOffline) then
    result := OwnNobraContactData;
end;

function TRosterDB.GetNick(jid, Nick: string): string;
begin
  if Nick = '' then
    result := jid
  else
    result := Nick;
end;

procedure TRosterDB.RegisterCallBackRosterView(RosterView: TObject);
var
  cbItem : PCallBackItem;
  i:integer;
begin
  i := SearchCallBackObj(RosterView,CallBackListRosterViewer);
  if i=-1 then begin
    new(cbItem);
    cbItem.jid := '';
    cbItem.CallBackObj := RosterView;
    CallBackListRosterViewer.add(cbItem);
  end;
end;

procedure TRosterDB.ClearCallBack;
var
  i:integer;
  cbItem:PCallBackItem;
begin
  for i := 0 to CallBackListRosterViewer.Count-1 do begin
    cbItem := CallBackListRosterViewer[i];
    dispose(cbItem);
  end;
  CallBackListRosterViewer.Clear;
end;

procedure TRosterDB.UnRegisterCallBackRosterView(RosterView: TObject);
var
  cbItem : PCallBackItem;
  i:integer;
begin
  i := SearchCallBackObj(RosterView,CallBackListRosterViewer);
  if i>-1 then begin
    cbItem := CallBackListRosterViewer[i];
    dispose(cbItem);
    CallBackListRosterViewer.Delete(i);
  end;
end;

procedure TRosterDB.RegisterCallBackChat(Chat: TObject; jid: string);
var
  cbItem: PCallBackItem;
begin
  jid := LowerCase(jid);
  New(cbItem);
  cbItem.jid := LowerCase(jid);
  cbItem.CallBackObj := chat;
  CallBackListChat.Add(cbItem);
end;

procedure TRosterDB.UnRegisterCallBackChat(Chat: TObject);
var
  cbItem: PCallBackItem;
  i: Integer;
begin
  i := 0;
  while i < CallBackListChat.Count do
  begin
    cbItem := CallBackListChat[i];
    if (cbItem.CallBackObj = chat) then
    begin
      cbItem := CallBackListChat[i];
      dispose(cbItem);
      CallBackListChat.Delete(i);
    end
    else
      Inc(i);
  end;
end;

function TRosterDB.SearchCallBackjid(jid: string): Integer;
var
  cbItem: PCallBackItem;
  i: Integer;
  s: string;
begin
  Result := -1;
  System.Delete(jid, pos('/',jid), Length(jid));
  jid := LowerCase(jid);
  for i := 0 to CallBackListChat.Count-1 do
  begin
    cbItem := CallBackListChat[i];
    s := cbItem.jid;
    System.Delete(s, Pos('/',s), Length(s));
    if (s = jid) then
    begin
      Result := i;
      Break;
    end;
  end;
end;

procedure TRosterDB.FireCallBackChat(jid: string; NewContactState, OldContactState: TContactState);
var
  cbItem: PCallBackItem;
  i: Integer;
begin
  if NewContactState<>OldContactState then
  begin
    i := SearchCallBackjid(jid);
    if i>-1 then
    begin
      cbItem := CallBackListChat[i];
      (cbItem.CallBackObj as TChatDistri).SetNewContactState(jid,NewContactState,OldContactState);
    end;
  end;
end;

procedure TRosterDB.FireCallBackRosterView(ContactData: PContactData; Action: TCBFireRosterViewAction);
var
  cbItem: PCallBackItem;
  i: Integer;
  cs: TContactState;
begin
  try
    i := 0;
    while i < CallBackListRosterViewer.Count do
    begin
      cbItem := CallBackListRosterViewer[i];
      case Action of
        cbRosterChange:     (cbItem.CallBackObj as TRosterViewer).RosterChange;
        cbPresenceChange:   (cbItem.CallBackObj as TRosterViewer).PresenceChange(ContactData);
        cbAvailChange:      (cbItem.CallBackObj as TRosterViewer).AvailChange(ContactData^);
        cbPresenceChanged:  begin
                              (cbItem.CallBackObj as TRosterViewer).PresenceChanged;
                              cs := OwnjabberContactData.status;
                              if (OwnjabberContactData.status = csOffline) and (OwnNobraContactData.status <> csOffline) then
                                cs := OwnNobraContactData.status;
                              FireCallBackChat('',cs,ContactData.status);
                            end;
        cbAvatarChanged:  begin
                              (cbItem.CallBackObj as TRosterViewer).MyAvatarChanged;
                            end;
        cbClose:            (cbItem.CallBackObj as TRosterViewer).close;
      end;
      Inc(i);
    end;
  except
  end;
end;

procedure TRosterDB.PresenceChanged();
begin
  FireCallBackRosterView(nil, cbPresenceChanged);
end;

procedure TRosterDB.clearJabber();
begin
  Clear(ntJabber);
end;

procedure TRosterDB.clearNobra();
begin
  Clear(ntNobraNet);
end;

end.
